/***
 * @Description: 本文件是定义以太网协议的相关接口函数和数据结构的C语言文件
 * @Author: Liverpool
 * @Date: 2024-05-17 06:43:46
 * @LastEditTime: 2024-05-17 06:44:41
 * @LastEditors: Liverpool
 */
#include "ethernet.h"
#include "net_Interface.h"
#include "debug.h"
#include "tools.h"
#include "protocol.h"
#include "system.h"
#include "arp.h"
#include "ipv4.h"

#if Debug_Display_Enable(Debug_Ethernet)

/***
 * @description: (静态函数)展示以太网的数据包内容
 * @Author: Liverpool
 * @Date: 2024-05-21 01:14:44
 * @return {*}
 * @param {char} *title 提示信息
 * @param {D_Ethernet_Packet} *packet 数据包的内存地址
 * @param {int} total_size 数据包的大小
 */
static void display_ethernet_packet(char *title, D_Ethernet_Packet *packet, int total_size)
{
    D_Ethernet_Header *header = &packet->header;
    plat_printf("--------------------%s-----------------\n", title);
    plat_printf("\t len: %d bytes\n", total_size);
    debug_dump_hwaddr("\t dest:", header->dest, ETHERNET_HW_SIZE);
    debug_dump_hwaddr("\t src:", header->src, ETHERNET_HW_SIZE);
    plat_printf("\t type:%04x\n", x_ntohs(header->protocol));
    switch (x_ntohs(header->protocol))
    {
    case Net_Protocol_ARP:
        plat_printf("\t protocol:ARP\n");
        break;
    case Net_Protocol_IPv4:
        plat_printf("\t protocol:IPV4\n");
        break;
    default:
        plat_printf("\t protocol:Unknown\n");
        break;
    }
    plat_printf("\n");
}
#else
#define display_ethernet_packet(title, packet, total_size)
#endif

/***
 * @description: (内部函数)以太网数据链路层的打开
 * @Author: Liverpool
 * @Date: 2024-05-17 07:46:56
 * @return {*} 在打开以太网链路层中出现的错误
 * @param {_Net_Interface} *netif 网络接口
 */
static net_err_t ethernet_open(struct _Net_Interface *netif)
{
    return arp_make_gratuitous(netif);
}
/***
 * @description: (内部函数)以太网数据链路层的关闭
 * @Author: Liverpool
 * @Date: 2024-05-17 07:46:56
 * @return {*}
 * @param {_Net_Interface} *netif 网络接口
 */
static void ethernet_close(struct _Net_Interface *netif) // 数据链路层的关闭
{
    arp_cache_clear(netif); // 在网络接口关闭的时候，需要把对应的ARP缓存表也要进行清空
}
/***
 * @description: (静态函数)判断以太网的帧是否合法
 * @Author: Liverpool
 * @Date: 2024-05-20 02:33:46
 * @return {*}  是否合法 0:合法  <0:不合法
 * @param {D_Ethernet_Packet} *frame 需要检查的帧的内存地址
 * @param {int} total_size 帧的总大小
 */
static net_err_t is_Ethernet_packet_ok(D_Ethernet_Packet *frame, int total_size)
{
    // 对以太网的帧结构的大小做出判断
    if (total_size > sizeof(D_Ethernet_Header) + ETHERNET_MTU)
    {
        debug_Warning(Debug_Ethernet, "frame size is too large:%d", total_size);
        return NET_ERR_SIZE;
    }
    else if (total_size < sizeof(D_Ethernet_Header))
    {
        debug_Warning(Debug_Ethernet, "frame size is too small:%d", total_size);
        return NET_ERR_SIZE;
    }
    return NET_ERR_OK;
}
/***
 * @description: (内部函数)以太网数据链路层的输入数据包的处理
 * @Author: Liverpool
 * @Date: 2024-05-17 07:46:56
 * @return {*} 在处理以太网链路层的输入数据包产生的错误
 * @param {_Net_Interface} *netif 网络接口
 * @param {D_Packet} *buf 输入的数据包地址
 */
static net_err_t ethernet_in(struct _Net_Interface *netif, D_Packet *buf) // 数据链路层的处理输入
{
    debug_info(Debug_Ethernet, "ethernet in...");
    packet_set_continuous(buf, sizeof(D_Ethernet_Header));                        // 至少需要以太网帧头的内存空间为连续
    D_Ethernet_Packet *packet = (D_Ethernet_Packet *)packet_firstBlock_data(buf); // 找到接收的数据包的包头位置，并进行以太网的数据解析
    net_err_t err = is_Ethernet_packet_ok(packet, buf->total_size);
    if (err < 0)
    {
        debug_Warning(Debug_Ethernet, "ethernet packet error!");
        return err;
    }
    // 进行进一步处理  TODO:
    display_ethernet_packet("Ethernrt in", packet, buf->total_size);
    // 对以太网传入的帧进行分类处理
    switch (x_ntohs(packet->header.protocol))
    {
    case Net_Protocol_ARP:
    {
        err = packet_remove_header(buf, sizeof(D_Ethernet_Header)); // 移除以太网的帧头
        if (err < 0)
        {
            debug_error(Debug_Ethernet, "remove header failed: %d ... ", err);
            return NET_ERR_SIZE;
        }
        return arp_in(netif, buf); // 对输入的ARP数据包进行输入解析
        break;
    }
    case Net_Protocol_IPv4:
    {
        arp_update_from_IP_packet(netif, buf); // 从IP数据包更新ARP缓存表
        err = packet_remove_header(buf, sizeof(D_Ethernet_Header));
        if (err < 0)
        {
            debug_error(Debug_Ethernet, "remove header failed: %d ... ", err);
            return NET_ERR_SIZE;
        }
        return ipv4_in(netif, buf); // 对输入的数据包做IPv4协议的解析
        break;
    }
    default:
        debug_Warning(Debug_Ethernet, "unkonwn packet...");
        return NET_ERR_NOT_SUPPORT;
    }
    return NET_ERR_OK;
}
/***
 * @description: (内部函数)以太网数据链路层的输出数据包的处理
 * @Author: Liverpool
 * @Date: 2024-05-17 07:46:56
 * @return {*} 在处理以太网链路层的输出数据包产生的错误
 * @param {_Net_Interface} *netif 网络接口
 * @param {D_IP_ADDR} *dest 输出目标IP地址
 * @param {D_Packet} *buf 输出的数据包地址
 */
static net_err_t ehternet_out(struct _Net_Interface *netif, D_IP_ADDR *dest, D_Packet *buf) // 数据链路层的处理输出
{
    if (ipaddr_is_equal(&netif->ipaddr, dest))
    {
        // 如果发送的IP地址和网卡的IP地址相同，那么发送的一定是IPv4协议
        return ethernet_raw_out(netif, Net_Protocol_IPv4, (const uint8_t *)netif->hwaddr.address, buf);
    }
    const uint8_t *hwaddr = arp_find(netif, dest);
    // 直接查找arp协议的mac地址，如果找到了，那就直接发送，否则就需要做arp协议处理
    if (hwaddr)
    {
        return ethernet_raw_out(netif, Net_Protocol_IPv4, hwaddr, buf);
    }
    else
    {
        return arp_resolve(netif, dest, buf); // arp协议的解析函数
    }
}
/***
 * @description: 以太网协议层的初始化,在数据链路层中注册相关的协议接口函数
 * @Author: Liverpool
 * @Date: 2024-05-17 06:49:34
 * @return {*} 在初始化过程中产生的错误
 */
net_err_t ethernet_init(void)
{
    static const D_Link_Layer _link_layer = {
        .type = NET_Interface_ETHER,
        .open = ethernet_open,
        .close = ethernet_close,
        .in = ethernet_in,
        .out = ehternet_out};
    debug_info(Debug_Ethernet, "init Ethernet");
    net_err_t err = net_interface_register_layer(NET_Interface_ETHER, &_link_layer);
    if (err < 0)
    {
        debug_error(Debug_Ethernet, "register error...");
        return err;
    }
    debug_info(Debug_Ethernet, "Ethernet init done...");
    return NET_ERR_OK;
}

/***
 * @description: 获取以太网发送的广播地址
 * @Author: Liverpool
 * @Date: 2024-05-22 08:11:00
 * @return {*} 存储以太网广播地址的数组内存地址
 */
const uint8_t *ethernet_broadcast_addr(void)
{
    static const uint8_t broadcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 直接定义广播地址
    return broadcast;
}

/***
 * @description: 发送以太网的原始数据帧给网卡
 * @Author: Liverpool
 * @Date: 2024-05-22 08:14:35
 * @return {*} 在发送过程中出现的错误
 * @param {D_Net_Interface} *netif 网络接口的内存地址
 * @param {uint16_t} protocol 以太网协议
 * @param {uint8_t} *dest 发送的目的地址
 * @param {D_Packet} packet 需要发送的数据包
 */
net_err_t ethernet_raw_out(D_Net_Interface *netif, uint16_t protocol, const uint8_t *dest, D_Packet *packet)
{
    int size = packet_total_size(packet);
    net_err_t err;
    // 对以太网的数据帧的大小进行检查
    if (size < ETHERNET_DATA_MIN)
    {
        debug_info(Debug_Ethernet, "reszie ethernet packet from %d to %d", size, ETHERNET_DATA_MIN);
        err = packet_resize(packet, ETHERNET_DATA_MIN);
        if (err < 0)
        {
            debug_error(Debug_Ethernet, "packet resize error: %d", err);
            return err;
        }
        // 需要给新补充的数据包填充数据
        packet_reset_access(packet);
        packet_seek(packet, size);
        packet_fill(packet, 0, ETHERNET_DATA_MIN - size);
        size = ETHERNET_DATA_MIN;
    }
    // 对以太网添加包头
    err = packet_add_header(packet, sizeof(D_Ethernet_Header), Packet_Header_Continuous);
    if (err < 0)
    {
        debug_error(Debug_Ethernet, " packet add header error: %d", err);
        return NET_ERR_SIZE;
    }
    //  对以太网的帧头进行赋值
    D_Ethernet_Packet *frame = (D_Ethernet_Packet *)packet_firstBlock_data(packet);
    plat_memcpy(frame->header.dest, dest, ETHERNET_HW_SIZE);
    plat_memcpy(frame->header.src, netif->hwaddr.address, ETHERNET_HW_SIZE);
    frame->header.protocol = x_htons(protocol);
    display_ethernet_packet("ethernet out", frame, size);

    if (plat_memcmp(frame->header.dest, netif->hwaddr.address, ETHERNET_HW_SIZE) == 0)
    {
        // 这里是给自己的网络接口发送数据，所以直接写入对应网卡的输入队列中
        return net_interface_put_in(netif, packet, -1);
    }
    else // 网卡需要向外部发送
    {
        err = net_interface_put_out(netif, packet, -1);
        if (err < 0)
        {
            debug_Warning(Debug_Ethernet, "put packet out error: %d", err);
            return err;
        }
    }
    return NET_ERR_OK;
}